package org.one.stone.soup.grfxML;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Stack;
import java.util.Vector;
import java.util.zip.GZIPInputStream;

import org.one.stone.soup.browser.Browser;
import org.one.stone.soup.file.FileHelper;
import org.one.stone.soup.grfx.ImageFilter;
import org.one.stone.soup.grfx.ShadowFilter;
import org.one.stone.soup.mjdb.data.field.LockException;
import org.one.stone.soup.xml.XmlDocument;
import org.one.stone.soup.xml.XmlParseException;
import org.one.stone.soup.xml.XmlParser;
/*
 * Copyright 2000-2001 Wet-Wired.com Ltd.,
 * Portsmouth England
 * All rights reserved.
 */

/**
	*
	* @author Nik Cross
*/
public class GrfxMLEngine implements Runnable{

	public static final String NAME		= "Giraffe Browser";
	public static final String VERSION	= "v1.8";
	public static final String BUILD	= "22/08/2006";

	public static String key;
	public String currentPage;

	private APInterface api;

	private int offsetX=0;
	private int offsetY=0;

	private XmlParser parser;

	protected GrfxMLProfile profile = new GrfxMLProfile();

	protected static boolean DEBUG=false;
	protected int MAX_WAIT=10000;

	private Stack pageStack=null;				//Page history stack

	protected DataTranslation DUMMY_T = new DataTranslation();	//A dummy translation used by base nodes

	protected DataBase BASE = new DataBase(this);				//The base node
	protected DataNode SYSTEM = new DataNode(this,null);				//The system graphics node
	protected Vector plugins = new Vector();				//A list of all plugins
	private Vector resources = new Vector();				//A list of the current pages resources

	protected Point mouseOffset = new Point(0,0);

	protected int geometryCount=0;

	protected RenderPipe backgroundPipe = new RenderPipe();				//A pipe of Geometries to render
	protected RenderPipe rootPipe = new RenderPipe();					//A pipe of Geometries to render
	protected RenderPipe guiPipe = new RenderPipe();					//A pipe of Geometries to render
	protected RenderPipe systemPipe = new RenderPipe();					//A pipe of Geometries to render

	protected boolean dropShadowBack = false;
	protected boolean dropShadowRoot = false;
	protected boolean dropShadowGui = false;

	//FPS = Frames per second
	protected int FRAME_WAIT=3;		//Time to wait between checking if next frame is to be built
	protected int FPS_SAMPLES=30;		//Number of samples to be made between calculating FPS achieved
	protected long sampleStart;		//Sample start time
	protected int sampleCount;		//Samples made
	protected int REAL_FPS=-1;		//The achieved FPS
	protected long benchMark=0;

	protected int FPS = 10;			//The desired FPS
	protected int timePerFrame;		//The FPS converted to time between frames
	protected long frameStart;		//The frame start time

	protected static int frameCounter = 0;	//The number of frames processed since engine start
	protected int waitCount = 0;

	protected boolean running=false;	//Flag to show engine is running
	protected boolean pause=false;
	protected boolean isPaused=false;
	protected boolean errors=false;
	protected String errorMessage="";
	protected boolean stopped=false;	//Flag to tell engine to stop
	protected Thread thread;			//The engine thread

	private GrfxMLController controller;	//The engines controller
	protected GrfxMLView view;		//The render panel

	protected Color defaultColor = Color.white;
	protected Font defaultFont = new Font("sanserif",Font.PLAIN,10);

public GrfxMLEngine(GrfxMLController owner,String alias,XmlDocument newSettings) {
	super();

	System.out.println(NAME+" ("+VERSION+") build "+BUILD);

	this.controller = owner;

	this.view = this.controller.getView();

	owner.setReady(this);

	thread = new Thread(this);
	thread.setName("Render Engine");
	thread.start();

	api = new APInterface(this,alias);

	parser = new XmlParser();
}
public void addPlugin(DataPlugin newPlugin)
{
	if(plugins.contains(newPlugin))
		return;

	plugins.addElement(newPlugin);
}
protected void buildPipe() {

	int width=BASE.SETTINGS.frameSize.getValueX();
	int height=BASE.SETTINGS.frameSize.getValueY();
	view.setSize(width,height);
	try{
		DUMMY_T.window.setValueXMin(0);
		DUMMY_T.window.setValueYMin(0);
		DUMMY_T.window.setValueX(width,this);
		DUMMY_T.window.setValueY(height,this);
	}
	catch(LockException le){}//dummy catch*/


	doInitialTranslation();

	backgroundPipe = new RenderPipe();
	rootPipe = new RenderPipe();
	guiPipe = new RenderPipe();

/*	for(int loop=0;loop<BASE.BACKGROUND.children.size();loop++)
	{
		buildRootPipe(BASE.BACKGROUND.children.get(loop),backgroundPipe);
	}*/
	buildRootPipe(BASE.BACKGROUND,backgroundPipe);

/*	for(int loop=0;loop<BASE.ROOT.children.size();loop++)
	{
		buildRootPipe(BASE.ROOT.children.get(loop),rootPipe);
	}*/
	buildRootPipe(BASE.ROOT,rootPipe);

/*	for(int loop=0;loop<BASE.GUI.children.size();loop++)
	{
		buildRootPipe(BASE.GUI.children.get(loop),guiPipe);
	}*/
	buildRootPipe(BASE.GUI,guiPipe);

	if(systemPipe==null || systemPipe.size()==0)
	{
		for(int loop=0;loop<SYSTEM.children.size();loop++)
		{
			buildRootPipe(SYSTEM.children.get(loop),systemPipe);
		}
	}

//System.out.println("Pipe Built");

/*	backgroundPipe = newBackgroundPipe;
	renderPipe = newRenderPipe;*/
}
private void buildRootPipe(DataNode rootNode,Vector pipe) {
	DataShape shape = rootNode.getShape();

	for(int loop=0;loop<shape.size();loop++)
	{
		DataGeometry geometry = shape.get(loop);
		if(geometry!=null)
		{
			Geometry geo = geometry.getGeometry(this);
			if(geo!=null)
			{
				pipe.addElement(geo);
			}
			else
			{
				pipe.addElement(geometry);
			}
		}
	}

	DataChildren children = rootNode.getChildren();

	for(int loop=0;loop<children.size();loop++)
	{
		buildRootPipe(children.get(loop),pipe);
	}
}

public void destroy()
{
	stopPlugins();
	stopProcessing();
	view = null;
}
protected void doInitialTranslation()
{
	BASE.translate(DUMMY_T);
	SYSTEM.translate(DUMMY_T);
}
public void draw(Graphics grfx) {

	//process();

	if(dropShadowBack==true || dropShadowRoot==true || dropShadowGui==true)
	{
		Image shadowMask = view.createImage();
		Graphics shadowMaskGrfx = shadowMask.getGraphics();
		shadowMaskGrfx.setColor(Color.white);
		shadowMaskGrfx.fillRect(0,0,view.getWidth(),view.getHeight());

		if(dropShadowBack==true)
			draw(backgroundPipe,shadowMaskGrfx);
		if(dropShadowRoot==true)
			draw(rootPipe,shadowMaskGrfx);
		if(dropShadowGui==true)
			draw(guiPipe,shadowMaskGrfx);

		view.setBackgroundImage( ImageFilter.filter( shadowMask,new ShadowFilter(),controller.getComponent() ) );

		dropShadowBack=false;
		dropShadowRoot=false;
		dropShadowGui=false;
	}

	draw(rootPipe,grfx);
	draw(guiPipe,grfx);
	draw(systemPipe,grfx);

	if(DEBUG)
	{
		drawDebug(grfx);
	}

	resetModelForNextFrame();
}
public void draw(Vector pipe,Graphics grfx) {

	if(pipe==null)
		return;

	grfx.setClip(0,0,BASE.SETTINGS.frameSize.getValueX(),BASE.SETTINGS.frameSize.getValueY());

	int loop=0;
	for(loop=0;loop<pipe.size();loop++)
	{
		Object o = pipe.elementAt(loop);
		if(o==null)
			continue;
		if(o instanceof Geometry)
		{
			((Geometry) o ).draw(grfx);
		}
		else if(o instanceof DataGeometry)
		{
			Geometry geo = ((DataGeometry)o).getGeometry(this);
			if(geo != null)
			{
				pipe.setElementAt(geo,loop);
			}
		}
		//geometryCount++;
	}

}
public void drawBackground(Graphics grfx) {

	int loop;
	for(loop=0;loop<backgroundPipe.size();loop++)
	{
		Geometry geo = (Geometry) backgroundPipe.elementAt(loop);
		geo.isBackground = true;
		geo.draw(grfx);
		geometryCount++;
	}

}
public void drawDebug(Graphics grfx) {

	grfx.setColor(Color.red);
	grfx.setFont(defaultFont);

	grfx.drawString("FRM NO:"+frameCounter+"  FPS:"+REAL_FPS+" BENCH MARK:"+benchMark,10,10);

	grfx.drawString(
			"MOUSE x:"+BASE.MOUSE.positionView.getValueX()+
			" y:"+BASE.MOUSE.positionView.getValueY()+
			" vx:"+BASE.MOUSE.velocityView.getValueX()+
			" vy:"+BASE.MOUSE.velocityView.getValueY()+
			" press:"+BASE.MOUSE.getButtonDown().getValue()

		,20,30);

/*	int x = BASE.MOUSE.positionView.getValueX()-400;
	int y = BASE.MOUSE.positionView.getValueY()-300;

	grfx.drawString("geometry:"+
		renderPipe.size()
	,20,50);

	grfx.drawString("nodes:"+
		grfxMLProcessor.nodeCount
	,20,70);

	grfx.drawString("z:"+
		grfxMLProcessor.z
	,20,90);

	grfx.drawString("over:"+
		BASE.MOUSE.getOver()
	,20,110);*/
}
public static void dumpgrfxML(ITag tag)
{
  try{
	System.out.println( /*new XmlParser().parseDocument( */tag.togrfxML("BASE")/* ).toXml()*/ );
  }
  catch(Exception e)
  {
	  e.printStackTrace();
	  return;
  }
}
/**
 *
 * @return org.one.stone.soup.xml.XmlParser
 */
protected XmlParser getParser() {
	return parser;
}
public boolean inFocus() {
	return view.inFocus();
}
private void initPage()
{
//	try{
		mouseOffset = controller.getMouseOffset();
		//BASE.SETTINGS.mouseOffset.setValueX(mouseOffset.width,this);
		//BASE.SETTINGS.mouseOffset.setValueY(mouseOffset.height,this);
//	}
//	catch(LockException le){}//dummy catch

	FPS = BASE.SETTINGS.fps.getValue();
	setFramesPerSecond(FPS);
//	System.out.println("DEBUG:"+DEBUG);
//	System.out.println("FPS:"+FPS);

	int width=BASE.SETTINGS.frameSize.getValueX();
	int height=BASE.SETTINGS.frameSize.getValueY();
	view.setSize(width,height);
	try{
		DUMMY_T.window.setValueXMin(0);
		DUMMY_T.window.setValueYMin(0);
		DUMMY_T.window.setValueX(width,this);
		DUMMY_T.window.setValueY(height,this);
	}
	catch(LockException le){}//dummy catch*/

	//controller.getContainer().setSize(width,height);
/*	Dimension newSize = owner.getContainer().getSize();
	try{
		BASE.SETTINGS.frameSize.setValueY(newSize.height,this);
		BASE.SETTINGS.frameSize.setValueX(newSize.width,this);
	}
	catch(LockException le){}//dummy catch*/

	/*if(DEBUG)*/ System.out.println("Frame width:"+width+" height:"+height);
//	buildPipe();
//	BASE.BACKGROUND.translate( DUMMY_T );
	view.setOffscreenReady(false);

}
public void initPlugins()
{
	for(int loop=0;loop<plugins.size();loop++)
	{
		DataPlugin plugin = (DataPlugin)plugins.elementAt(loop);
		if(plugin.getInitialized()==false)
		{
			plugin.setInitialized(true);
			plugin.initialize();
			plugin.process();
		}
	}
}

public void load(InputStream iStream,String resourceName) {

	if(pageStack==null)
	{
		pageStack = new Stack();
	}

	plugins = new Vector();

	controller.setWaiting(true);
	if(DEBUG) System.out.println("Engine paused. Started grfxML load");

	if(iStream==null)
	{
		return;
	}

	BufferedInputStream bStream = new BufferedInputStream(iStream);

	XmlDocument doc = loadXml(bStream,resourceName);

	if(doc==null)
	{
		return;
	}

	pause=true;
	BASE.MOUSE._cursorMode=Cursor.WAIT_CURSOR;

	if(DEBUG) System.out.println("loaded grfxML");

//	bmParsinggrfxML.stop();
//	bmBuildingModel.start();

//	pause=true;

	stopPlugins();
	BASE = new DataBase(this);
	plugins = new Vector();

	BASE.buildFromgrfxML(doc);

	if(DEBUG) System.out.println("grfxML built");

	register();

	if(DEBUG) System.out.println("data registered");

	buildPipe();

	if(DEBUG) System.out.println("pipe built");
	process();
	process();

	if(DEBUG) System.out.println("initial processing done");

	initPlugins();

	if(DEBUG) System.out.println("plugins init done");

	initPage();

	if(DEBUG) System.out.println("page init done");

	BASE.MOUSE._cursorMode=Cursor.DEFAULT_CURSOR;

	MouseEvent me = new MouseEvent(
			controller.getComponent(),
			MouseEvent.MOUSE_MOVED,
			System.currentTimeMillis(),
			0,
			0,
			0,
			0,
			false
		);
	BASE.MOUSE.resetEvents();
	BASE.MOUSE.addEvent( me );

	pause=false;

	if(DEBUG) System.out.println("engine pause released");

	controller.setWaiting(false);
	controller.sendCommand("Ready");

//	bmBuildingModel.stop();

//	org.one.stone.soup.lab.TheSlab.report();
}
public synchronized void loadPage(String urlName) {

	currentPage = urlName;

	if(pageStack==null)
	{
		pageStack = new Stack();
	}
	else
	{
		BASE.MOUSE.resetButtons();

		if(BASE.SETTINGS.cache.getValue()==true)
		{
			GrfxMLPageHolder page = new GrfxMLPageHolder(urlName,BASE,backgroundPipe,rootPipe,guiPipe,plugins,resources);
			pageStack.push(page);
		}

//		bImg = ImageFilter.filter(preImg,this);
	}

	if(plugins!=null)
	{
		stopPlugins();
	}
	plugins = new Vector();
	resources = new Vector();

	controller.setWaiting(true);
	if(DEBUG) System.out.println("Engine paused. Started grfxML load");

	InputStream iStream=null;
	try{
		iStream = Browser.setBaseURL(urlName);

		if(FileHelper.getFileExtension(urlName).equals("gz"))
		{
			iStream = new GZIPInputStream(iStream);
		}
	}
	catch(IOException ioe)
	{
		controller.showLoadError(ioe,urlName);
		unloadPage();
		return;
	}

	if(iStream==null)
	{
		return;
	}

	BufferedInputStream bStream = new BufferedInputStream(iStream);

	XmlDocument doc = loadXml(bStream,urlName);

	if(doc==null)
	{
		return;
	}

	pause=true;
	BASE.MOUSE._cursorMode=Cursor.WAIT_CURSOR;

	if(DEBUG) System.out.println("loaded grfxML");

//	bmParsinggrfxML.stop();
//	bmBuildingModel.start();

//	pause=true;

	stopPlugins();
	BASE = new DataBase(this);
	plugins = new Vector();

	BASE.buildFromgrfxML(doc);

	if(DEBUG) System.out.println("grfxML built");

	register();

	if(DEBUG) System.out.println("data registered");

	buildPipe();

	if(DEBUG) System.out.println("pipe built");
	process();
	process();

	if(DEBUG) System.out.println("initial processing done");

	initPlugins();

	if(DEBUG) System.out.println("plugins init done");

	initPage();

	if(DEBUG) System.out.println("page init done");

	BASE.MOUSE._cursorMode=Cursor.DEFAULT_CURSOR;

	/*MouseEvent me = new MouseEvent(
			controller.getComponent(),
			MouseEvent.MOUSE_MOVED,
			System.currentTimeMillis(),
			0,
			0,
			0,
			0,
			false
		);*/
	BASE.MOUSE.resetEvents();
	//BASE.MOUSE.addEvent( me );

	pause=false;

	if(DEBUG) System.out.println("engine pause released");

	controller.setWaiting(false);
	controller.sendCommand("Ready");

//	bmBuildingModel.stop();

//	org.one.stone.soup.lab.TheSlab.report();

	//startGui();
}
public void loadPage(URL url) {
	loadPage( url.toString() );
}
public void loadPage(DataString file) {
	loadPage( file.getValue() );
}
public void process() {

 try{

	profile.BEHAVIOUR_LOAD.setValue(GrfxMLProcessor.behaviourCount,this);
	profile.ACTION_LOAD.setValue(GrfxMLProcessor.actionCount,this);
	profile.TRANSLATION_LOAD.setValue(GrfxMLProcessor.translationCount,this);
	profile.NODE_LOAD.setValue(GrfxMLProcessor.nodeCount,this);
	profile.GEOMETRY_LOAD.setValue(geometryCount,this);

	geometryCount = 0;
	GrfxMLProcessor.resetCounters();

	BASE.MOUSE.cutEvents();

	if(BASE.MOUSE.getCutEventsSize()>0)
	{
		BASE.MOUSE._cursorMode=Cursor.DEFAULT_CURSOR;
	}

	if(BASE.MOUSE.getCutEventsSize()>0)
	{
		controller.setCursor( Cursor.getPredefinedCursor( BASE.MOUSE._cursorMode ) );

		for(int loop=0;loop<BASE.MOUSE.getCutEventsSize();loop++)
		{
			MouseEvent e = (MouseEvent)BASE.MOUSE.getCutEvent(loop);

			switch(e.getID())
			{
				case MouseEvent.MOUSE_PRESSED:
				case MouseEvent.MOUSE_RELEASED:
					GrfxMLProcessor.processMouseEvent(this,e,BASE.MOUSE);
			}
		}

		/*GrfxMLProcessor.copy(BASE.MOUSE.positionView,BASE.MOUSE.position);
		GrfxMLProcessor.copy(BASE.MOUSE.lastPositionView,BASE.MOUSE.lastPosition);
		GrfxMLProcessor.copy(BASE.MOUSE.pressPositionView,BASE.MOUSE.pressPosition);
		GrfxMLProcessor.copy(BASE.MOUSE.velocityView,BASE.MOUSE.velocity);*/
	}

	GrfxMLProcessor.z = -1;
	BASE.BACKGROUND.translate(DUMMY_T);

	GrfxMLProcessor.z = -1;
	BASE.ROOT.translate(DUMMY_T);

	GrfxMLProcessor.z = -1;
	BASE.GUI.translate(DUMMY_T);

	GrfxMLProcessor.z = -1;
	SYSTEM.translate(DUMMY_T);

	GrfxMLProcessor.process(BASE.PROCESS);
	GrfxMLProcessor.process(SYSTEM.process);

	if(BASE.MOUSE.getCutEventsSize()>0)
	{
		controller.setCursor( Cursor.getPredefinedCursor( BASE.MOUSE._cursorMode ) );

		for(int loop=0;loop<BASE.MOUSE.getCutEventsSize();loop++)
		{
			MouseEvent e = (MouseEvent)BASE.MOUSE.getCutEvent(loop);
			GrfxMLProcessor.processMouseEvent(this,e,BASE.MOUSE);
		}

		GrfxMLProcessor.copy(BASE.MOUSE.positionView,BASE.MOUSE.position);
		GrfxMLProcessor.copy(BASE.MOUSE.lastPositionView,BASE.MOUSE.lastPosition);
		GrfxMLProcessor.copy(BASE.MOUSE.pressPositionView,BASE.MOUSE.pressPosition);
		GrfxMLProcessor.copy(BASE.MOUSE.velocityView,BASE.MOUSE.velocity);
	}

	DataPlugin plugin;
	for(int loop=0;loop<plugins.size();loop++)
	{
		plugin = (DataPlugin)plugins.elementAt(loop);
		if(plugin.getInitialized()==true && plugin.parent!=null && plugin.parent.running.getValue()==true)
		{
			plugin.process();
		}
	}

 }catch(LockException le){}//dummy catch

}
public void register() {

	try{
		BASE._DATA.add(BASE.SETTINGS,this);
		BASE._DATA.add(BASE.PROCESS,this);
		BASE._DATA.add(BASE.MOUSE,this);
		BASE._DATA.add(BASE.INFO,this);
		BASE._DATA.add(BASE.KEY,this);
		BASE._DATA.add(BASE.BACKGROUND,this);
		BASE._DATA.add(BASE.ROOT,this);
	}catch(LockException le){}//dummy catch

	BASE.SETTINGS.register(BASE._DATA);
	BASE.PROCESS.register(BASE._DATA);
	BASE.DATA.register(BASE._DATA);
	BASE.MOUSE.register(BASE._DATA);
	BASE.INFO.register(BASE._DATA);
	BASE.KEY.register(BASE._DATA);
	BASE.BACKGROUND.register(BASE._DATA);
	BASE.ROOT.register(BASE._DATA);
	BASE.GUI.register(BASE._DATA);

	if(systemPipe==null || systemPipe.size()==0)
	{
		DataNode sys = new DataNode(this,null);
		api.loadTo("jar://system.node",sys,SYSTEM.getChildren());
	}
		DataStore SYSTEM_data = new DataStore(this);
		SYSTEM.register(SYSTEM_data);
		SYSTEM.data = GrfxMLStoreProcessor.initialize(SYSTEM_data);
//	}

	if(DEBUG) System.out.println("BASE Data registered");

	BASE._DATA = GrfxMLStoreProcessor.initialize(BASE._DATA);

	if(DEBUG) System.out.println("Data Initialized");

}

public void registerResource(String url)
{
	resources.addElement(url);
}

public Vector getResourceList()
{
	return resources;
}

public void reloadPage() {

	pause=true;
	controller.setWaiting(true);

	InputStream iStream = null;
	XmlDocument doc=null;

	if(plugins!=null)
	{
		stopPlugins();
	}
	plugins = new Vector();
	try{
		//URL url = new URL(Browser.getBase());
		//URL url = new URL(currentPage );
		//iStream = url.openStream();
	    iStream = Browser.getInputStream(currentPage);
	}
	catch(IOException ioe)
	{
		ioe.printStackTrace();
		unloadPage();
		return;
	}

	BufferedInputStream bStream = new BufferedInputStream(iStream);

	try{
		doc = parser.parseDocument(bStream);
		bStream.close();
	}
	catch(XmlParseException xe)
	{
		errorMessage = xe.getMessage();
		System.out.println(errorMessage);
		errors=true;
		return;
	}
	catch(FileNotFoundException e)
	{
		errorMessage = "Failed to find the file "+e.getMessage();
		System.err.println(errorMessage);
		errors=true;
		return;
	}
	catch(Exception e)
	{
		errorMessage = e.toString();
		System.err.println(errorMessage);
		errors=true;
		e.printStackTrace();
		return;
	}

//	pause=true;

	stopPlugins();

	BASE = new DataBase(this);
	BASE.buildFromgrfxML(doc);

	register();

	buildPipe();
	process();

	initPlugins();
	initPage();

	MouseEvent me = new MouseEvent(
			controller.getComponent(),
			MouseEvent.MOUSE_MOVED,
			System.currentTimeMillis(),
			0,
			0,
			0,
			0,
			false
		);
	BASE.MOUSE.resetEvents();
	BASE.MOUSE.addEvent( me );

	pause=false;
	controller.setWaiting(false);
	controller.sendCommand("Ready");
}

private XmlDocument loadXml(InputStream iStream,String fileName)
{
	XmlDocument doc = null;
	BufferedInputStream bStream = new BufferedInputStream(iStream);

	try{
		doc = parser.parseDocument(bStream);
		bStream.close();
	}
	catch(XmlParseException xe)
	{
		controller.showLoadError(xe,fileName);

		errors=true;
		return null;
	}
	catch(FileNotFoundException e)
	{
		controller.showLoadError(e,fileName);

		errors=true;
		return null;
	}
	catch(Exception e)
	{
		controller.showLoadError(e,fileName);

		errors=true;
		e.printStackTrace();
		return null;
	}

	return doc;
}

public void resetModelForNextFrame()
{
	try{
		BASE.resetChanged();
		SYSTEM.resetChanged();

		BASE.KEY.resetChanged();

		GrfxMLProcessor.copy(
			BASE.MOUSE.positionView,
			BASE.MOUSE.lastPositionView
		);

		BASE.MOUSE.velocityView.setValueX(0,BASE);
		BASE.MOUSE.velocityView.setValueY(0,BASE);
		BASE.MOUSE.buttonPressed.setValue(false,BASE);
		BASE.MOUSE.buttonReleased.setValue(false,BASE);
		BASE.MOUSE.buttonDoubleClicked.setValue(false,BASE);

		BASE.MOUSE.button1Pressed.setValue(false,BASE);
		BASE.MOUSE.button1Released.setValue(false,BASE);
		BASE.MOUSE.button1DoubleClicked.setValue(false,BASE);

		BASE.MOUSE.button2Pressed.setValue(false,BASE);
		BASE.MOUSE.button2Released.setValue(false,BASE);
		BASE.MOUSE.button2DoubleClicked.setValue(false,BASE);
	}catch(LockException le){}//dummy catch
}


public void run() {

	if(running==true)
		return;

	running=true;

	frameCounter=0;
	if(DEBUG) System.out.println("render engine started");

	sampleCount=0;
	sampleStart=System.currentTimeMillis();
	frameStart=System.currentTimeMillis();
	
	setFramesPerSecond(FPS);
	
	waitCount=0;
	long benchMarkAcc=0;

	boolean titleSet = false;

	while(running)
	{
 try{
		if(FPS!=ProcessConstant.FREE_RUN)
		{
			benchMarkAcc += System.currentTimeMillis()-frameStart;
			if(frameCounter%100==0)
			{
				benchMark = benchMarkAcc/100;
				benchMarkAcc=0;
			}

			while(System.currentTimeMillis()-frameStart<timePerFrame)
			{
				try{Thread.sleep(FRAME_WAIT);}catch(Exception e){}
				//System.out.print(".");
			}
		}

		frameStart=System.currentTimeMillis();

		if(!errors)
		{
			if(!pause)
			{
				isPaused=false;
				waitCount=0;

				if(titleSet==false)
				{
					setTitle();
					titleSet=true;
				}
			}
			else
			{
				isPaused=true;
				titleSet=false;
				waitCount++;

				/*if(waitCount>MAX_WAIT)
				{
					errorMessage = "Failed to load the file "+Browser.getBase()+" in time";
					errors=true;
					return;
				}*/
			}
		}

		if(view!=null)
		{
			view.draw();
		}

		frameCounter++;

		sampleCount++;
// calc FPS
		if(sampleCount>=FPS_SAMPLES)
		{
			REAL_FPS = 1000/((int)(System.currentTimeMillis()-sampleStart)/FPS_SAMPLES);
			sampleCount=0;
			sampleStart=System.currentTimeMillis();
			if(DEBUG) System.out.println("REAL_FPS:"+REAL_FPS);
		}
 }
 catch(Exception e)
 {
	 if(DEBUG) e.printStackTrace();
 }
	}

	running=false;
	if(DEBUG) System.out.println("render engine stopped");
	stopped=true;
}
/**
 *
 * @param newParser org.one.stone.soup.xml.XmlParser
 */
protected void setParser(XmlParser newParser) {
	parser = newParser;
}

public void setMouseOffset(Point newMouseOffset)
{
	mouseOffset = newMouseOffset;
}

private void setTitle()
{
	if(!BASE.INFO.title.getValue().equals(""))
	{
			controller.setTitle(BASE.INFO.title.getValue());
	}
	else
	{
		controller.setTitle("Giraffe");
	}
}
public void stopPlugins()
{
	for(int loop=0;loop<plugins.size();loop++)
	{
		DataPlugin plugin = (DataPlugin)plugins.elementAt(loop);
		plugin.stop();
		plugin.setInitialized(false);
	}
}
public void stopProcessing() {

	running=false;

	while(stopped==false)
	{
		if(DEBUG) System.out.println("waiting for render engine to stop");

		try{Thread.sleep(100);}catch(Exception e){};
	}
}
public void unloadPage() {

	if(pageStack==null)
		return;

	if(pageStack.isEmpty())
		return;

	controller.setWaiting(true);
	pause=true;
	stopPlugins();

	GrfxMLPageHolder page = (GrfxMLPageHolder)pageStack.pop();

	BASE = page.BASE;
	BASE.MOUSE.resetButtons();

	plugins = page.plugins;
	resources = page.resources;

	try{
		if(page.url!=null)
		{
			Browser.setBaseURL(page.url);
		}
	}
	catch(Exception e)
	{
		e.printStackTrace();
	}

	backgroundPipe = page.backgroundPipe;
	rootPipe = page.rootPipe;
	guiPipe = page.guiPipe;

	initPlugins();
	initPage();

	MouseEvent me = new MouseEvent(
			controller.getComponent(),
			MouseEvent.MOUSE_MOVED,
			System.currentTimeMillis(),
			0,
			0,
			0,
			0,
			false
		);
	BASE.MOUSE.resetEvents();
	BASE.MOUSE.addEvent( me );

	pause=false;
	controller.setWaiting(false);
	controller.sendCommand("Ready");
}
public int getOffsetX()
{
	return offsetX;
}
public int getOffsetY()
{
	return offsetY;
}
public GrfxMLView getView()
{
	return view;
}
public GrfxMLController getController()
{
	return controller;
}
	/**
	 * @return Returns the running.
	 */
	public boolean isRunning() {
		return running;
	}
	/**
	 * @param dropShadowBack The dropShadowBack to set.
	 */
	public void setDropShadowBack(boolean dropShadowBack) {
		this.dropShadowBack = dropShadowBack;
	}
	/**
	 * @param dropShadowGui The dropShadowGui to set.
	 */
	public void setDropShadowGui(boolean dropShadowGui) {
		this.dropShadowGui = dropShadowGui;
	}
	/**
	 * @param dropShadowRoot The dropShadowRoot to set.
	 */
	public void setDropShadowRoot(boolean dropShadowRoot) {
		this.dropShadowRoot = dropShadowRoot;
	}
	/**
	 * @param view The view to set.
	 */
	public void setView(GrfxMLView view) {
		this.view = view;
	}

	public APInterface getAPI()
	{
		return api;
	}

	public boolean hasChanged()
	{
		if(
	        rootPipe.hasChanged() ||
	        guiPipe.hasChanged() ||
	        systemPipe.hasChanged()
	    )
		{
		    return true;
		}

		return false;
	}
	public void resetChanged()
	{
	    rootPipe.resetChanged();
        guiPipe.resetChanged();
        systemPipe.resetChanged();
	}
	
	public void setFramesPerSecond(int fps)
	{
		timePerFrame = 1000/(fps+1);
		
		System.out.println("FPS:"+fps+" Time per frame:"+timePerFrame);
	}
}
